package com.nulldev.util.java;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;

import com.nulldev.util.FinalBypass;
import com.nulldev.util.io.FileUtil;
import com.nulldev.util.os.OSUtil;
import com.nulldev.util.other.CharsetUtil;
import com.nulldev.util.threading.legacy.ThreadExecutor;

public class ProcessUtil {

	private static final LinkedList<String> commands = new LinkedList<String>();
	
	public static String execCmd_NoQueue(final String cmd) throws java.io.IOException {
	    final Process proc = Runtime.getRuntime().exec(cmd);
	    java.io.InputStream is = proc.getInputStream();
	    java.util.Scanner s = new java.util.Scanner(is, CharsetUtil.US_ASCII.name()).useDelimiter("\\A");
	    String val = "";
	    if (s.hasNext()) {
	        val = s.next();
	    }
	    else {
	        val = "";
	    }
	    is.close();
	    s.close();
	    try {
			proc.waitFor();
		} catch (InterruptedException e) {
		    proc.destroy();
		}
	    return val;
	}
	
	public static String execCmd(final String cmd) throws java.io.IOException {
		if (commands.isEmpty()) {
			return execCmd_NoQueue(cmd);
		}else {
			commands.add(cmd);
			while (!commands.getFirst().equals(cmd)) {
				try {
					Thread.sleep(50L);
				} catch (InterruptedException e) {
				}
			}
			final String out = execCmd_NoQueue(cmd);
			commands.removeFirst();
			return out;
		}
	}
	
	public static String runWithEnviromentVariables(final String command) throws Exception {
		if (!OSUtil.isLinux()) {
			return execCmd(command);
		}
		final File tmp = FileUtil.createTempFile("sh");
		FileUtil.writeFile(tmp, command);
		tmp.setExecutable(true);
		final String result = execCmd("sh -c " + tmp.toString());
		tmp.delete();
		return result;
	}
	
	public static class ProcessUtilENV {
		private static final LinkedList<DataSet<String, String[]>> commands = new LinkedList<DataSet<String, String[]>>();
		
		public static String execCmd_NoQueue(final DataSet<String, String[]> cmd) throws java.io.IOException {
		    final Process proc = Runtime.getRuntime().exec(cmd.getA(), cmd.getB());
		    System.out.println(cmd);
		    java.io.InputStream is = proc.getInputStream();
		    java.util.Scanner s = new java.util.Scanner(is, CharsetUtil.US_ASCII.name()).useDelimiter("\\A");
		    String val = "";
		    if (s.hasNext()) {
		        val = s.next();
		    }
		    else {
		        val = "";
		    }
		    is.close();
		    s.close();
		    try {
				proc.waitFor();
			} catch (InterruptedException e) {
			    proc.destroy();
			}
		    return val;
		}
		
		public static String execCmd(final DataSet<String, String[]> cmd) throws java.io.IOException {
			if (commands.isEmpty()) {
				return execCmd_NoQueue(cmd);
			}else {
				commands.add(cmd);
				while (!commands.getFirst().equals(cmd)) {
					try {
						Thread.sleep(50L);
					} catch (InterruptedException e) {
					}
				}
				final String out = execCmd_NoQueue(cmd);
				commands.removeFirst();
				return out;
			}
		}
	}
	
	public static class ProcessUtilPB {
		private static final LinkedList<ProcessBuilder> commands = new LinkedList<ProcessBuilder>();
		
		public static String execCmd_NoQueue(final ProcessBuilder cmd) throws java.io.IOException {
			cmd.redirectErrorStream(true);
		    final Process proc = cmd.start();
		    java.io.InputStream is = proc.getInputStream();
		    java.util.Scanner s = new java.util.Scanner(is, CharsetUtil.US_ASCII.name()).useDelimiter("\\A");
		    String val = "";
		    if (s.hasNext()) {
		        val = s.next();
		    }
		    else {
		        val = "";
		    }
		    is.close();
		    s.close();
		    try {
				proc.waitFor();
			} catch (InterruptedException e) {
			    proc.destroy();
			}
		    return val;
		}
		
		public static String execCmd(final ProcessBuilder cmd) throws java.io.IOException {
			if (commands.isEmpty()) {
				return execCmd_NoQueue(cmd);
			}else {
				commands.add(cmd);
				while (!commands.getFirst().equals(cmd)) {
					try {
						Thread.sleep(50L);
					} catch (InterruptedException e) {
					}
				}
				final String out = execCmd_NoQueue(cmd);
				commands.removeFirst();
				return out;
			}
		}
	}
	
	public static class ProcessUtilActive {
		public static void execActiveCmd_NoQueue(final String cmd, final ProcessUtilRead event) throws java.io.IOException {
		    final Process proc = Runtime.getRuntime().exec(cmd);
		    final java.io.InputStream is = proc.getInputStream();
		    final InputStreamReader isr = new InputStreamReader(is);
		    final BufferedReader reader = new BufferedReader(isr);
		    final FinalBypass<Boolean> stop = new FinalBypass<Boolean>(false);
//		    java.util.Scanner s = new java.util.Scanner(is, CharsetUtil.US_ASCII.name()).useDelimiter("\\A");
//		    String val = "";
//		    if (s.hasNext()) {
//		        val = s.next();
//		    }
//		    else {
//		        val = "";
//		    }
//		    s.close();
		    ThreadExecutor.execute(new Runnable() {
				
				@Override
				public void run() {
					while (true) {
						if (stop.getObject()) {
							break;
						}
						try {
							final String read = reader.readLine();
							if (read != null) {
								event.onRead(read);
							}
						} catch (IOException e) {
						}
					}
				}
			});
		    try {
				proc.waitFor();
				stop.setObject(true);
				reader.close();
			    is.close();
			} catch (InterruptedException e) {
			    proc.destroy();
			    stop.setObject(true);
			    reader.close();
			    is.close();
			}
		}
		
		private static final LinkedList<Command> commands = new LinkedList<Command>();
		public static void execCmd(final String cmd, final ProcessUtilRead event) throws java.io.IOException {
			if (commands.isEmpty()) {
				execActiveCmd_NoQueue(cmd, event);
			}else {
				commands.add(new Command(cmd, event));
				while (!commands.getFirst().getCommand().equals(cmd)) {
					try {
						Thread.sleep(50L);
					} catch (InterruptedException e) {
					}
				}
				execActiveCmd_NoQueue(cmd, event);
				commands.removeFirst();
			}
		}
		
		public static void execActiveCmd_NoQueue_UseProcessBuilder(final ProcessBuilder cmd, final ProcessUtilRead event) throws java.io.IOException {
		    final Process proc = cmd.start();
		    final java.io.InputStream is = proc.getInputStream();
		    final InputStreamReader isr = new InputStreamReader(is);
		    final BufferedReader reader = new BufferedReader(isr);
		    final FinalBypass<Boolean> stop = new FinalBypass<Boolean>(false);
//		    java.util.Scanner s = new java.util.Scanner(is, CharsetUtil.US_ASCII.name()).useDelimiter("\\A");
//		    String val = "";
//		    if (s.hasNext()) {
//		        val = s.next();
//		    }
//		    else {
//		        val = "";
//		    }
//		    s.close();
		    ThreadExecutor.execute(new Runnable() {
				
				@Override
				public void run() {
					while (true) {
						if (stop.getObject()) {
							break;
						}
						try {
							final String read = reader.readLine();
							if (read != null) {
								event.onRead(read);
							}
						} catch (IOException e) {
						}
					}
				}
			});
		    try {
				proc.waitFor();
				stop.setObject(true);
				reader.close();
			    is.close();
			} catch (InterruptedException e) {
			    proc.destroy();
			    stop.setObject(true);
			    reader.close();
			    is.close();
			}
		}
		
		private static class Command {

			private final String command;
			private final ProcessUtilRead event;
			public Command(final String cmd, final ProcessUtilRead event) {
				this.command = cmd;
				this.event = event;
			}
			
			public String getCommand() {
				return this.command;
			}
			
			public ProcessUtilRead getEvent() {
				return this.event;
			}
		}
	}
	
	public static class ProcessUtilBytes {
		
	}
	
	public static interface ProcessUtilRead {
		final ProcessUtilRead NULL = new ProcessUtilRead() {
			
			@Override
			public void onRead(String input) {
			}
		};

		public void onRead(final String input);
	}
}
